﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
using System.ComponentModel;

namespace Isuka.Cola.Win32
{
    public class Win32DynamicLibrary
        : Isuka.Auxiliaries.DisposableObject
        , IDynamicLibrary
        , IDisposable
    {
        [DllImport("kernel32", CharSet = CharSet.Auto, ExactSpelling = false)]
        private extern static IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPTStr)]string lpFileName);

        [DllImport("kernel32", ExactSpelling = true)]
        private extern static bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true)]
        private extern static IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)]string lpProcName);

        IntPtr dllHandle;

        public Win32DynamicLibrary(string libraryPath)
        {
            Contract.Requires(!string.IsNullOrWhiteSpace(libraryPath));

            dllHandle = LoadLibrary(libraryPath);
            if (dllHandle == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
        }

        public Delegate GetDelegate(Type signature, string name = null)
        {
            VerifyNotDisposed();
            IntPtr ptr = GetProcAddress(dllHandle, name ?? signature.Name);
            if (ptr == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            var d = Marshal.GetDelegateForFunctionPointer(ptr, signature);
            Contract.Assume(d != null);
            return d;
        }

        #region Overrides

        protected override bool IsDisposed
        {
            get
            {
                return dllHandle == IntPtr.Zero || base.IsDisposed;
            }
        }

        #endregion
    }
}
